﻿using System;
using System.Collections.Generic;
using System.Text;

using Nemerle.Utility;
using Nemerle.Collections;

namespace Nemerle.Xml
{
  using Node;
  using Namespace;
  using DocumentElement;

  /// <summary>
  /// Ptints out any type of xml element
  /// </summary>
  [StringTemplate.StringTemplateGroup]
  public sealed class Printer
  {
    Context : Context {get; set;}

    public this()                  { Context = Context.Default; }
    public this(context : Context) { Context = context; }

    private RemoveWhitespaces(nodes : list[Node]) : list[Node]
    {
      nodes.Filter(n => !(n is Whitespace))
    }

    public Print(xml : ElementBase) : string
    {
      | Text(value)                                 => Escape(value)
      | Whitespace                                  => ""
      | Element(_, _, _, [], [Text(text)]) as el    => PrintTextNodeInline(el, text)
      | Element(_, _, _, attrs, [Text(text)]) as el => PrintTextNodeInline(el, attrs, text)
      | Element(_, _, _, [], []) as node            => PrintEmptyNode(node)
      | Element(_, _, _, _, [])  as node            => PrintNodeWithoutSubnodes(node)
      | Element(_, _, _, attrs, nodes) as el        => PrintNode(el, attrs, RemoveWhitespaces(nodes))
      | Document(instrs, nodes)                     => PrintDocument(instrs, RemoveWhitespaces(nodes))
      | ProcInstruction(name, value)                => PrintProcessingInstruction(name, value)
      | null                                        => ""
      | _                                           => throw ArgumentException("", "xml")
    }

    PrintDocument(instrs : list[ProcInstruction], nodes : list[Node]) : string {<#
    ..$(instrs; "\n"; i => PrintProcessingInstruction(i.name, i.value))
    ..$(nodes; "\n"; Print)
    #>}

    PrintProcessingInstruction(name : string, value : string) : string {<#
    <?$name $value?>
    #>}

    PrintEmptyNode(node : Node.Element) : string {<#<$(PrintElementName(node))/>#>}

    PrintNodeWithoutSubnodes(node : Node.Element) : string {<#
    <$(PrintElementName(node))$(AddSpaceIfNotEmpty(node.attrs))$(PrintAttrs(node.attrs))/>
    #>}

    PrintNode(node : Node.Element, attrs : list[Attr], nodes : list[Node]) : string {<#
    <$(PrintElementName(node))$(AddSpaceIfNotEmpty(attrs))$(PrintAttrs(attrs))>
      ..$(nodes; "\n"; Print)
    </$(PrintElementName(node))>
    #>}

    PrintTextNodeInline(node : Element, text : string) : string {<#
    <$(PrintElementName(node))>$text</$(PrintElementName(node))>
    #>}

    PrintTextNodeInline(node : Element, attrs : list[Attr], text : string) : string {<#
    <$(PrintElementName(node))$(AddSpaceIfNotEmpty(attrs))$(PrintAttrs(attrs))>$text</$(PrintElementName(node))>
    #>}

    PrintAttrs(attrs : list[Attr]) : string {<#
    ..$(attrs; " ";)
    #>}

    PrintElementName(el : Element) : string
    {
      match (el.ns)
      {
        | Empty => $<#$(el.name)#>
        | _     => if (!string.IsNullOrEmpty(el.prefix))
                     $<#$(el.prefix):$(el.name)#>
                   else
                    $<#$(el.name)#>
      }
    }

    Escape(value : string) : string { value } // not yet work correctlly

    AddSpaceIfNotEmpty[T](lst : list[T]) : string
    {
      if (lst.IsEmpty) "" else " "
    }
  }
}
